﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;

namespace DSPCalculator.mechanics
{
    public class Visualizer
    {
        public Bitmap _bitmap;
        private Graphics g;
        private Pen pen;
        private Font font;
        private Brush brush;
        public float singleWidth = 150;
        public float singleHeight = 100;
        public float xoffset = 10;
        public float yoffset = 10;
        public float radius = 5;
        public Visualizer(Bitmap bitmap)
        {
            _bitmap= bitmap;
            g = Graphics.FromImage(bitmap);
            g.Clear(Color.White);
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            pen = new Pen(Color.Black,2f);
            font = new Font("微软雅黑", 10);
            brush = new SolidBrush(Color.Black);
        }
        public void Draw(Tree tree,float sheer)
        {
            Dictionary<Node, PointF> posInfo = new Dictionary<Node, PointF>();
            List<Node>[] layers = new List<Node>[tree.maxLayer + 1];
            for(int i = 0; i <= tree.maxLayer; i++)
            {
                List<Node> targets = tree.nodes.FindAll(x => x.layer == i);
                layers[i] = targets;
                for(int j = 0; j < targets.Count; j++)
                {
                    PointF pos = GetPosOnMap(i, j);
                    posInfo.Add(targets[j], pos);
                }
            }
            //for(int i = 0; i <= tree.maxLayer-1; i++)
            //{
            //    float totalDiff = 0;
            //    int branchNum = 0;
            //    for(int j = 0; j < layers[i].Count; j++)
            //    {
            //        Node n = layers[i][j];
            //        for(int k = 0; k < n.branches.Count; k++)
            //        {
            //            //totalDiff += (posInfo[n].X - posInfo[n.branches[k]].X)/(n.branches[k].layer-n.layer) + sheer;
            //            totalDiff += (posInfo[n].X - posInfo[n.branches[k]].X) + sheer;
            //            branchNum++;
            //        }
            //    }
            //    totalDiff /= branchNum;
            //    List<Node> moving = tree.nodes.FindAll(x => x.layer <= i);
            //    for(int j = 0; j < moving.Count; j++)
            //    {
            //        PointF ori = posInfo[moving[j]];
            //        PointF nw = new PointF(ori.X - totalDiff, ori.Y);
            //        posInfo[moving[j]] = nw;
            //    }
            //}
            for(int i = 1; i <= tree.maxLayer;i++)
            {
                float totalDiff = 0;
                int branchNum = 0;
                for(int j = 0; j < layers[i].Count; j++)
                {
                    Node n = layers[i][j];
                    List<Node> N_using = tree.nodes.FindAll(x => x.branches.Contains(n));
                    for(int k = 0; k < N_using.Count; k++)
                    {
                        totalDiff += (posInfo[n].X - posInfo[N_using[k]].X) + sheer;
                        branchNum++;
                    }
                }
                totalDiff /= branchNum;
                for (int j = 0; j < layers[i].Count; j++)
                {
                    PointF ori = posInfo[layers[i][j]];
                    PointF nw = new PointF(ori.X - totalDiff, ori.Y);
                    posInfo[layers[i][j]] = nw;
                }
            }

            float minX = xoffset;
            for(int i = 0; i < tree.nodes.Count; i++)
            {
                if (posInfo[tree.nodes[i]].X < minX)
                    minX = posInfo[tree.nodes[i]].X;
            }
            float moveRight = xoffset - minX;
            for (int i = 0; i < tree.nodes.Count; i++)
            {
                PointF ori = posInfo[tree.nodes[i]];
                PointF nw = new PointF(ori.X + moveRight, ori.Y);
                posInfo[tree.nodes[i]] = nw;
            }

            for (int i = 0; i < tree.nodes.Count; i++)
            {
                PointF pos = posInfo[tree.nodes[i]];
                g.DrawEllipse(pen, pos.X - radius, pos.Y - radius, 2 * radius, 2 * radius);
                string a = "";
                int machineNum_R = (int)Math.Ceiling(tree.nodes[i].machineNum);
                if (tree.nodes[i].item.machine == "")
                    a = a = $"{tree.nodes[i].item.name}{tree.nodes[i].needSpeed}/s";
                else
                    a = $"{tree.nodes[i].item.name}{tree.nodes[i].needSpeed}/s\n{machineNum_R}台{tree.nodes[i].item.machine}";
                g.DrawString(a, font, brush, pos);
            }

            foreach (Node n in tree.nodes)
            {
                if (n.branches.Count==0)
                    continue;
                PointF s = posInfo[n];
                foreach(Node b in n.branches)
                {
                    PointF e = posInfo[b];
                    //g.DrawLine(pen,s, e);
                    g.DrawBezier(pen, s,new PointF(s.X,(s.Y+e.Y)/2), new PointF(e.X, (e.Y+s.Y)/2), e);
                }
            }

            Dictionary<string, int> totalMachineNum = new Dictionary<string, int>();
            foreach(Node n in tree.nodes)
            {
                string machine = n.item.machine;
                if (machine == "")
                    continue;
                int num = (int)Math.Ceiling(n.machineNum);
                if (totalMachineNum.ContainsKey(machine))
                {
                    totalMachineNum[machine] += num;
                }
                else
                {
                    totalMachineNum.Add(machine, num);
                }
            }
            PointF statistics = new PointF(xoffset, tree.maxLayer * singleHeight + 6*font.Size+2*yoffset);
            StringBuilder sb = new StringBuilder();
            foreach(var pair in totalMachineNum)
            {
                sb.Append(pair.Value + "台" + pair.Key+"\n");
            }
            g.DrawString(sb.ToString(),font,brush,statistics);
            g.Dispose();
        }
        private PointF GetPosOnMap(int layer,int index)
        {
            float x = (float)(xoffset + index * singleWidth);
            float y = (float)(yoffset + layer * singleHeight);
            return new PointF(x, y);
        }
    }
}
